﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Management;
using System.Text;
using System.Threading.Tasks;

namespace Reflexive.IO
{
    public class WMIReader<TConverter,TDestination> : IDisposable, IReflexive<TConverter, ManagementBaseObject, TDestination>
        where TConverter : ITypeConverter<ManagementBaseObject, TDestination>, new()
        where TDestination : class
    {

        // Define the Common Information Model (CIM) path  
        // for WIM monitoring.  
        ManagementEventWatcher watcher;
        SynchronizedCollection<Subscription<TDestination>> _observers = new SynchronizedCollection<Subscription<TDestination>>();

        public StatusCode Status
        {
            get;
            private set;
        }

        public WMIReader(string path, string query)
        {
            // Define the Common Information Model (CIM) path  
            // for WIM monitoring.  

            // Create a managed object watcher as  
            // defined in System.Management. 
            watcher = new ManagementEventWatcher(query);

            // Set the watcher options.
            TimeSpan timeInterval = TimeSpan.MaxValue;
            watcher.Options = new EventWatcherOptions(null, timeInterval, 1);

            // Set the scope of the WMI events to  
            // watch to be ASP.NET applications.
            watcher.Scope = new ManagementScope(new ManagementPath(path));
        }

        public void Start()
        {
            if (Status == StatusCode.Completed)
            {
                throw new NotSupportedException("Reader is in the Completed state.");
            }

            watcher.EventArrived += (sender, args) =>
            {

                try
                {
                    _observers.OnNext<TDestination>((Convertible<TConverter, ManagementBaseObject, TDestination>)args.NewEvent);
                }
                catch (Exception e)
                {
                    System.Diagnostics.Trace.TraceError(e.ToString());
                }

            };
            watcher.Start();
            Status = StatusCode.Started;


        }

        public void Stop()
        {
            try
            {
                if (Status == StatusCode.Started)
                {
                    watcher.Stop();
                }
            }
            catch
            {
                throw;
            }
            finally
            {
                Status = StatusCode.Suspended;
            }
        }

        #region Subscription Management
      
        public IDisposable Subscribe(IObserver<TDestination> observer)
        {
            var subscription = new Subscription<TDestination>(observer);
            subscription.UnSubscribe += UnSubscribe;
            _observers.Add(subscription);
            return subscription;
        }

        private void UnSubscribe(object sender, IObserver<TDestination> e)
        {
            _observers.Remove(((Subscription<TDestination>)sender));
        }

        #endregion

        public void Dispose()
        {
            if (Status != StatusCode.Completed)
            {
                try
                {
                    watcher.Stop();
                    watcher.Dispose();
                    _observers.OnCompleted<TDestination>();
                }
                catch (Exception e)
                {
                    System.Diagnostics.Trace.TraceError(e.ToString());
                }
                finally
                {
                    Status = StatusCode.Completed;
                }
            }
        }
    }
}

